<?php

namespace x_mier\utils;

/**
 * 文档类
 * Class Rsa
 */
class Doc
{
    protected $actionkey = [
        "title" => "未定义方法标题",
        "desc" => "未定义方法描述",
        "author" => "橘子",
        "version" => "1.0",
        "method" => '',
        "param" => [],
        "return" => [],
        "model" => [],
        "url" => '',
    ];
    protected $module = [];
    protected $public_key;
    public function __construct()
    {
        $config = config('plugin.x_mier.doc.app');
        if ($config && $config['enable']) {
            $this->module = $config['module'];
        }
        $this->public_key = "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgVODivvmNYsNJw/URrCssadSe
YtaiNVAWO3PXFwsNutNjEnBNWXvIaDmCbv/IO7Kc7lVbmUqxPejM+F3yofh5nzCj
FMIdhcB2vD413FbMGgma+tvx4tt7jj6+1ZVT21UzYoauW2+vbEUjgX9B9ASlDoUB
cO2xhA4apNbfOiQoFQIDAQAB
-----END PUBLIC KEY-----";
    }

    public function get_api_list()
    {
        $sign = request()->header('sign');
        if ($sign) {
            $data =  new Rsa($this->public_key);
            $config = input();
            if ($data->verify(json_encode($config), $sign)) {
                $version = $config['version'] ?? '/app/';
                $filter_method = array_merge(['__construct', '__destruct'], $config['filter_method'] ?? []);
                $list = $this->get_list($version, $filter_method);
                return json($list);
            }
        }
    }

    protected function get_list($path, $filter_method = [])
    {
        $files = $this->listDirFiles($path);
        $list = [];
        foreach ($files as $key => $classes) {
            $children = [];
            foreach ($classes as $index => $class) {
                $data = $this->get_class($class, $filter_method);
                if ($data) {
                    $data['pid'] = $index;
                    $children[$index] = $data;
                }
            }
            if (!empty($children)) {
                if (empty($this->module[$key])) {
                    $list = array_merge($list, array_values($children));
                } else {
                    $list[] = [
                        'title' => trim($this->module[$key] ?? $key),
                        'pid' => $key,
                        'children' => array_values($children)
                    ];
                }
            }
        }
        return $list;
    }

    private function listDirFiles($app, $isapp = true, $pid = '')
    {
        $arr = [];
        $base = base_path();
        $dir = $isapp ? $base . $app . 'controller' : $app;
        if (!is_dir($dir)) {
            return $arr; // 如果目录不存在，直接返回空数组
        }
        $iterator = new \FilesystemIterator($dir, \FilesystemIterator::SKIP_DOTS);
        foreach ($iterator as $fileInfo) {
            $fileName = $fileInfo->getFilename();
            if ($fileInfo->isDir()) {
                // 递归处理子目录
                $arr = array_merge($arr, $this->listDirFiles($fileInfo->getPathname(), false, $fileName));
            } elseif ($fileInfo->isFile() && $fileName !== 'File.php' && $fileInfo->getExtension() === 'php') {
                $arr[$fileName][] = str_replace([$base, '/', '.php'], ['', '\\', ''], $fileInfo->getPathname());
            }
        }
        asort($arr);
        return $arr;
    }

    private function get_class($class, $filter_method = [])
    {
        $list = [];
        if (!class_exists($class)) {
            return $list; // 提前返回，避免不必要的处理
        }
        list($list,$reflection) = $this->getDocComment($class);
        if (empty($list) || empty($list['title'])) {
            return $list; // 提前返回，避免不必要的处理
        }
        $list['class'] = $class;
        $methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
        foreach ($methods as $key => $method) {
            if (in_array($method->name, $filter_method) || '\\' . $method->class !== $class) {
                continue; // 跳过不需要的方法
            }
            $comment = $this->parse_class($method);
            if (empty($comment)) {
                continue; // 跳过没有注释的方法
            }
            $className = strtr($method->class, [
                'plugin\\' => 'app\\',
                '\\' => '/',
                'app\\controller\\' => '/',
            ]);
            $comment['url'] = trim(strtolower($className) . '/' . $method->name, '/');
            $comment['method'] = $comment['method'] ?? 'POST'; // 使用 null 合并运算符
            if (!empty($comment['title'])) {
                $list['children'][] = array_merge($this->actionkey, $comment);
            }
        }
        return $list;
    }
    private function getDocComment($comment)
    {
        $reflection = new \ReflectionClass($comment);
        $doc_str = $reflection->getDocComment();
        return [$this->parse_class($doc_str),$reflection];
    }
    private function parse_class($comment)
    {
        $comment = preg_replace('/[ ]+/', ' ', $comment);
        preg_match_all('/\*[\s+]?@(.*?)[\n|\r]/is', $comment, $matches);
        $arr = array_map(function ($match) {
            return explode(' ', $match);
        }, $matches[1]);
        return $this->parseCommentArray($arr);
    }
    private function parseCommentArray(array $array = [])
    {
        $newArr = [];
        foreach ($array as $item) {
            $field = trim(strtolower($item[0]));
            if (in_array($field, ['model', 'param', 'return','property'])) {
                if (class_exists($item[1])) {
                    list($doc) = $this->getDocComment($item[1]);
                    $newArr[$field] = $doc['property'] ?? $doc['param'];
                } else {
                    $newArr[$field][] = count($item) > 3 ? $this->getParam($item)  : $this->getProperty($item);
                }
            } else {
                $newArr[$item[0]] = isset($item[1]) ? $item[1] : '-';
            }
        }
        return $newArr;
    }
    private function getParam($item)
    {
        return [
            'type' => $item[1],
            'name' => preg_replace('/\$/i', '', ($item[2]??'')),
            'default' => isset($item[4]) ? ($item[4]??'') : '-',
            'desc' => isset($item[3]) ? ($item[3]??'') : '-',
            'valid' => isset($item[5]) ? ($item[5]??'') : 'NO',
        ];
    }
    private function getProperty($item)
    {
        return [
            'type' => $item[1],
            'name' => preg_replace('/\$/i', '', $item[2] ?? ''),
            'desc' => isset($item[3]) ? $item[3] : '-',
        ];
    }
}
